/* Copyright (C) 2014  Jean-Sebastien Castonguay
 *
 * This file is part of TechnoBoard.
 *
 * TechnoBoard is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TechnoBoard is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with TechnoBoard.  If not, see <http://www.gnu.org/licenses/>. */

#include "technoboard.h"
#include "driver_timer.h"
#include "system.h"
#include <GenericTypeDefs.h>


/* Division by 8 since T1_PS_1_8 setting. Division by 2 since peripheral clock
 * (i.e. TCY = FOSC / 2). */
#define TIMER_CLK_PER_MS  (SYSCLK / (2 * 8 * MS_PAR_S))
#define TIMER_CLK_PER_TICK  ((TIMER_CLK_PER_MS * TIMER_TICK_PERIOD_IN_MS) - 1)

#if (TIMER_CLK_PER_TICK > 0xFFFF)
#error "__FILE__: TIMER_CLK_PER_TICK exceeds 16 bits. "
#endif

static volatile Timer_Tick timerTick_ms = 0;


void DriverTimer_Init() {
    ConfigIntTimer1( T1_INT_PRIOR_3 | T1_INT_ON);
    OpenTimer1( T1_ON | T1_PS_1_8 | T1_SOURCE_INT, TIMER_CLK_PER_TICK);
}


Timer_Tick Timer_GetTick() {

    Timer_Tick TickCopy;

    INTERRUPT_PROTECT( TickCopy = timerTick_ms);

    return TickCopy;
}


Timer_ms Timer_GetElapseTime( Timer_Tick referenceTime) {
    return (Timer_GetTick() - referenceTime);
}


static inline void WaitOneMS() {

    // TODO Erreur, on doit appeler Timer_GetTick() pour protection cas de course
    Timer_Tick startingTick = timerTick_ms;
    
    uint32_t timerClkToWaitFor = ReadTimer1() + TIMER_CLK_PER_MS;
    uint32_t WrapAroudOffset = 0;

    do {
        if ((timerTick_ms - startingTick) > 0) {
            WrapAroudOffset = TIMER_CLK_PER_TICK;
        }
    } while ((ReadTimer1() + WrapAroudOffset) < timerClkToWaitFor);
}


void Timer_Wait( Timer_ms delay) {
    while (delay) {
        delay--;
        WaitOneMS();
    }
}


void __attribute__((__interrupt__, __shadow__, __auto_psv__)) _T1Interrupt(void) {
    T1_Clear_Intr_Status_Bit;
    timerTick_ms += TIMER_TICK_PERIOD_IN_MS;
}

